using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;

// Copyright 2009 (c) SocketDemo.

namespace SocketDemo
{
    public delegate void MessageReceive(ClientConnection sender, string Data);
    public delegate void RemoteHostClose(ClientConnection sender);

    /// <summary>
    /// Client Connection
    /// </summary>
    public class ClientConnection
    {
        #region field

        const int READ_BUFFER_SIZE = 100000;
        private byte[] readBuffer = new byte[READ_BUFFER_SIZE];
        
        public event MessageReceive OnMessageReceived;
        public event RemoteHostClose OnRemoteHostClosed;

        private TcpClient _Client;

        public TcpClient Client
        {
            get
            {
                return _Client;
            }
        }

        #endregion

        #region method

        public ClientConnection(TcpClient client)
        {
            try
            {
                _Client = client;

                client.GetStream().BeginRead(readBuffer, 0, READ_BUFFER_SIZE,
                    new AsyncCallback(StreamReceiver), client);
            }
            catch (NullReferenceException ex)
            {
                ExceptionHelper.ShowException(ex);
            }
            catch (ObjectDisposedException ex)
            {
                client.Close();
                ExceptionHelper.ShowException(ex);
            }
            catch (IOException ex)
            {
                client.Close();
                ExceptionHelper.ShowException(ex);
            }
        }

        private void StreamReceiver(IAsyncResult ar)
        {
            TcpClient client = null;
            int intBytesRead = 0;
            string strReceiveData = string.Empty;
            NetworkStream objNetworkStream = null;

            try
            {
                client = (TcpClient)ar.AsyncState;

                if (!client.Connected)
                {
                    client.Close();
                    LogHelper.ShowLog("Close\t" + DateTime.Now.ToString() + "\t" +
                        client.GetHashCode().ToString());
                    return;
                }

                try
                {
                    objNetworkStream = client.GetStream();
                }
                catch (ObjectDisposedException ex)
                {
                    client.Close();
                    ExceptionHelper.ShowException(ex);
                    return;
                }
                catch (InvalidOperationException ex)
                {
                    client.Close();
                    ExceptionHelper.ShowException(ex);
                    return;
                }

                try
                {
                    lock (objNetworkStream)
                    {
                        intBytesRead = objNetworkStream.EndRead(ar);
                    }
                }
                catch (IOException ex)
                {
                    if (ex.InnerException is SocketException)
                    {
                        client.Close();
                        ExceptionHelper.ShowException(ex);
                       
                        return;
                    }
                    else
                    {
                        client.Close();
                        ExceptionHelper.ShowException(ex);
                        return;
                    }
                }

                if (intBytesRead > 0)
                {
                    strReceiveData = Encoding.UTF8.GetString(readBuffer, 0, intBytesRead);
                    OnMessageReceived(this, strReceiveData);
                }
                else
                {
                    OnRemoteHostClosed(this);

                    if (objNetworkStream != null)
                    {
                        objNetworkStream.Close();
                    }

                    client.Close();
                    return;

                }

                if (!client.Connected)
                {
                    client.Close();
                    LogHelper.ShowLog("client close\t" + DateTime.Now.ToString() + "\t" + 
                        client.GetHashCode().ToString());
                    return;
                }

                try
                {
                    lock (objNetworkStream)
                    {
                        objNetworkStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, 
                            new AsyncCallback(StreamReceiver), client);
                    }
                }
                catch (ObjectDisposedException ex)
                {
                    client.Close();
                    ExceptionHelper.ShowException(ex);
                }
                catch (IOException ex)
                {
                    client.Close();
                    ExceptionHelper.ShowException(ex);
                }
            
            }
            catch (Exception ex)
            {
                client.Close();
                ExceptionHelper.ShowException(ex);
            }
        }

        #endregion
    }
}
